/*
 * FreeModbus Libary: user callback functions and buffer define in master mode
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: user_mb_app_m.c,v 1.60 2013/11/23 11:49:05 Armink $
 */
#include "user_mb_app.h"

/*-----------------------Master mode use these variables----------------------*/
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
//Master mode:DiscreteInputs variables
USHORT   usMDiscInStart                             = M_DISCRETE_INPUT_START;
#if      M_DISCRETE_INPUT_NDISCRETES%8
	UCHAR    ucMDiscInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_DISCRETE_INPUT_NDISCRETES / 8 + 1];
#else
	UCHAR    ucMDiscInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_DISCRETE_INPUT_NDISCRETES / 8];
#endif
//Master mode:Coils variables
USHORT   usMCoilStart                               = M_COIL_START;
#if      M_COIL_NCOILS%8
	UCHAR    ucMCoilBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_COIL_NCOILS / 8 + 1];
#else
	UCHAR    ucMCoilBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_COIL_NCOILS / 8];
#endif
//Master mode:InputRegister variables
USHORT   usMRegInStart                              = M_REG_INPUT_START;
USHORT   usMRegInBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_INPUT_NREGS];
//Master mode:HoldingRegister variables
USHORT   usMRegHoldStart                            = M_REG_HOLDING_START;
USHORT   usMRegHoldBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_HOLDING_NREGS];

/**
 * Modbus master input register callback function.
 *
 * @param pucRegBuffer input register buffer
 * @param usAddress input register address
 * @param usNRegs input register number
 *
 * @return result
 */
eMBErrorCode eMBMasterRegInputCB(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
	eMBErrorCode    eStatus = MB_ENOERR;
	USHORT          iRegIndex;
	USHORT*         pusRegInputBuf;
	USHORT          REG_INPUT_START;
	USHORT          REG_INPUT_NREGS;
	USHORT          usRegInStart;

	pusRegInputBuf = usMRegInBuf[ucMBMasterGetDestAddress() - 1];
	REG_INPUT_START = M_REG_INPUT_START;
	REG_INPUT_NREGS = M_REG_INPUT_NREGS;
	usRegInStart = usMRegInStart;

	/* it already plus one in modbus function method. */
	usAddress--;

	if((usAddress >= REG_INPUT_START)
	        && (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS)) {
		iRegIndex = usAddress - usRegInStart;

		while(usNRegs > 0) {
			pusRegInputBuf[iRegIndex] = *pucRegBuffer++ << 8;
			pusRegInputBuf[iRegIndex] |= *pucRegBuffer++;
			iRegIndex++;
			usNRegs--;
		}
	} else {
		eStatus = MB_ENOREG;
	}

	return eStatus;
}

/**
 * Modbus master holding register callback function.
 *
 * @param pucRegBuffer holding register buffer
 * @param usAddress holding register address
 * @param usNRegs holding register number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBMasterRegHoldingCB(UCHAR* pucRegBuffer, USHORT usAddress,
                                   USHORT usNRegs, eMBRegisterMode eMode)
{
	eMBErrorCode    eStatus = MB_ENOERR;
	USHORT          iRegIndex;
	USHORT*         pusRegHoldingBuf;
	USHORT          REG_HOLDING_START;
	USHORT          REG_HOLDING_NREGS;
	USHORT          usRegHoldStart;

	pusRegHoldingBuf = usMRegHoldBuf[ucMBMasterGetDestAddress() - 1];
	REG_HOLDING_START = M_REG_HOLDING_START;
	REG_HOLDING_NREGS = M_REG_HOLDING_NREGS;
	usRegHoldStart = usMRegHoldStart;
	/* if mode is read, the master will write the received date to buffer. */
	eMode = MB_REG_WRITE;

	/* it already plus one in modbus function method. */
	usAddress--;

	if((usAddress >= REG_HOLDING_START)
	        && (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS)) {
		iRegIndex = usAddress - usRegHoldStart;

		switch(eMode) {
			/* read current register values from the protocol stack. */
			case MB_REG_READ:
				while(usNRegs > 0) {
					*pucRegBuffer++ = (UCHAR)(pusRegHoldingBuf[iRegIndex] >> 8);
					*pucRegBuffer++ = (UCHAR)(pusRegHoldingBuf[iRegIndex] & 0xFF);
					iRegIndex++;
					usNRegs--;
				}

				break;

			/* write current register values with new values from the protocol stack. */
			case MB_REG_WRITE:
				while(usNRegs > 0) {
					pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
					pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
					iRegIndex++;
					usNRegs--;
				}

				break;
		}
	} else {
		eStatus = MB_ENOREG;
	}

	return eStatus;
}

/**
 * Modbus master coils callback function.
 *
 * @param pucRegBuffer coils buffer
 * @param usAddress coils address
 * @param usNCoils coils number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
                                 USHORT usNCoils, eMBRegisterMode eMode)
{
	eMBErrorCode    eStatus = MB_ENOERR;
	USHORT          iRegIndex, iRegBitIndex, iNReg;
	UCHAR*          pucCoilBuf;
	USHORT          COIL_START;
	USHORT          COIL_NCOILS;
	USHORT          usCoilStart;
	iNReg =  usNCoils / 8 + 1;

	pucCoilBuf = ucMCoilBuf[ucMBMasterGetDestAddress() - 1];
	COIL_START = M_COIL_START;
	COIL_NCOILS = M_COIL_NCOILS;
	usCoilStart = usMCoilStart;

	/* if mode is read,the master will write the received date to buffer. */
	eMode = MB_REG_WRITE;

	/* it already plus one in modbus function method. */
	usAddress--;

	if((usAddress >= COIL_START)
	        && (usAddress + usNCoils <= COIL_START + COIL_NCOILS)) {
		iRegIndex = (USHORT)(usAddress - usCoilStart) / 8;
		iRegBitIndex = (USHORT)(usAddress - usCoilStart) % 8;

		switch(eMode) {
			/* read current coil values from the protocol stack. */
			case MB_REG_READ:
				while(iNReg > 0) {
					*pucRegBuffer++ = xMBUtilGetBits(&pucCoilBuf[iRegIndex++],
					                                 iRegBitIndex, 8);
					iNReg--;
				}

				pucRegBuffer--;
				/* last coils */
				usNCoils = usNCoils % 8;
				/* filling zero to high bit */
				*pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
				*pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
				break;

			/* write current coil values with new values from the protocol stack. */
			case MB_REG_WRITE:
				while(iNReg > 1) {
					xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, 8,
					               *pucRegBuffer++);
					iNReg--;
				}

				/* last coils */
				usNCoils = usNCoils % 8;

				/* xMBUtilSetBits has bug when ucNBits is zero */
				if(usNCoils != 0) {
					xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
					               *pucRegBuffer++);
				}

				break;
		}
	} else {
		eStatus = MB_ENOREG;
	}

	return eStatus;
}

/**
 * Modbus master discrete callback function.
 *
 * @param pucRegBuffer discrete buffer
 * @param usAddress discrete address
 * @param usNDiscrete discrete number
 *
 * @return result
 */
eMBErrorCode eMBMasterRegDiscreteCB(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
	eMBErrorCode    eStatus = MB_ENOERR;
	USHORT          iRegIndex, iRegBitIndex, iNReg;
	UCHAR*          pucDiscreteInputBuf;
	USHORT          DISCRETE_INPUT_START;
	USHORT          DISCRETE_INPUT_NDISCRETES;
	USHORT          usDiscreteInputStart;
	iNReg =  usNDiscrete / 8 + 1;

	pucDiscreteInputBuf = ucMDiscInBuf[ucMBMasterGetDestAddress() - 1];
	DISCRETE_INPUT_START = M_DISCRETE_INPUT_START;
	DISCRETE_INPUT_NDISCRETES = M_DISCRETE_INPUT_NDISCRETES;
	usDiscreteInputStart = usMDiscInStart;

	/* it already plus one in modbus function method. */
	usAddress--;

	if((usAddress >= DISCRETE_INPUT_START)
	        && (usAddress + usNDiscrete    <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES)) {
		iRegIndex = (USHORT)(usAddress - usDiscreteInputStart) / 8;
		iRegBitIndex = (USHORT)(usAddress - usDiscreteInputStart) % 8;

		/* write current discrete values with new values from the protocol stack. */
		while(iNReg > 1) {
			xMBUtilSetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8,
			               *pucRegBuffer++);
			iNReg--;
		}

		/* last discrete */
		usNDiscrete = usNDiscrete % 8;

		/* xMBUtilSetBits has bug when ucNBits is zero */
		if(usNDiscrete != 0) {
			xMBUtilSetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex,
			               usNDiscrete, *pucRegBuffer++);
		}
	} else {
		eStatus = MB_ENOREG;
	}

	return eStatus;
}
#endif
